package cn.xing.modules.app.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import cn.xing.common.exception.RRException;
import cn.xing.common.utils.PageUtils;
import cn.xing.common.utils.Query;
import cn.xing.enums.ChartTimeUnitEnum;
import cn.xing.enums.OrderConfirmReceivingEnum;
import cn.xing.enums.OrderRefundEnum;
import cn.xing.enums.OrderStatusEnum;
import cn.xing.modules.app.dao.OrderDao;
import cn.xing.modules.app.dto.OrderDto;
import cn.xing.modules.app.dto.OrderItemDto;
import cn.xing.modules.app.dto.PayAsyncDto;
import cn.xing.modules.app.entity.*;
import cn.xing.modules.app.service.*;
import cn.xing.modules.app.vo.PayVo;
import cn.xing.modules.sys.service.RefundInfoService;
import cn.xing.modules.sys.service.SysConfigService;
import cn.xing.modules.sys.vo.OrderCountVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.xing.common.constant.SysConfigConstans.SYS_AUTO_ACCEPT_ORDER;

@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    private SysNotificationService sysNotificationService;
    @Autowired
    private UserReminderService userReminderService;
    @Autowired
    private RefundInfoService refundInfoService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private DeliveryLogService deliveryLogService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private ProductService productService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ProductSaleAttrRelationServiceImpl productSaleAttrRelationService;

    @Autowired
    private AlipayService alipayService;

    @Override
    public void managerRefuseOrder(Long id, Long operatorId) {
        OrderEntity orderEntity = getById(id);
        // 非 已付款
        if (!OrderStatusEnum.PAYED.getCode().equals(orderEntity.getOrderStatus())) {
            throw new RRException("只有已付款的订单才能拒绝");
        }
        doCancelAndRefund(orderEntity, "商家拒绝接单");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void managerDeliveryOrder(Long id, Long operatorId) {
        doUpdateOrderStatus(id, OrderStatusEnum.SEND_OUT);

        OrderEntity orderEntity = getById(id);
        deliveryLogService.start(orderEntity.getOrderSn());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void managerConfirmSendedOrder(Long id, Long operatorId) {
        doUpdateOrderStatus(id, OrderStatusEnum.REACH);

        OrderEntity orderEntity = getById(id);
        deliveryLogService.sended(orderEntity.getOrderSn());
    }

    @Override
    public void managerAcceptOrder(Long id, Long operatorId) {
        doUpdateOrderStatus(id, OrderStatusEnum.TAKEN);
    }

    @Override
    public void managerCancelOrder(Long id, Long operatorId) {
        OrderEntity orderEntity = getById(id);
        OrderStatusEnum orderStatusEnum = OrderStatusEnum.getTypeByValue(orderEntity.getOrderStatus());
        switch (orderStatusEnum) {
            case BUS_DEALING:
                doCancelAndRefund(orderEntity, "商家同意用户取消");

            case CANCEL:
                break;
            case NEW:
                doCancel(orderEntity.getId(), "商家取消");
                break;
            case PAYED:
                // 已付款，未送出，需要取消订单+退款
                doCancelAndRefund(orderEntity, "商家取消");
                break;
            case TAKEN:
                // 商家已接单，需要商家确认后，才能退款
                doCancelAndRefund(orderEntity, "商家取消");
                break;
            case SEND_OUT:
                doCancelAndRefund(orderEntity, "商家取消");
                break;
            default:
                throw new RRException(String.format("当前订单[%s],状态为[%s],无法取消", orderEntity.getOrderSn(), orderStatusEnum.getDescription()));
        }

        userReminderService.onOrderCancel(orderEntity);
    }

    @Override
    public List<OrderEntity> queryPayed(String startTime, String endTime) {
        return this.baseMapper.selectList(
                new QueryWrapper<OrderEntity>()
                        .ge("order_status", OrderStatusEnum.PAYED.getCode())
                        .between(StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime), "create_time", startTime, endTime)

        );
    }


    @Transactional
    @Override
    public void userCancelOrder(Long userId, String orderSn) {
        OrderEntity orderEntity = getByOrderSn(orderSn);

        checkIllegal(orderEntity, userId);

        // 状态在 "已送出" 之前，都可以直接取消。
        OrderStatusEnum orderStatusEnum = OrderStatusEnum.getTypeByValue(orderEntity.getOrderStatus());
        switch (orderStatusEnum) {
            // case REFUNDED:
            //     break;
            // case CANCEL:
            //     break;
            case NEW:
                doCancel(orderEntity.getId(), "用户主动取消");
                break;
            case PAYED:
                // 已付款，未送出，需要取消订单+退款
                doCancelAndRefund(orderEntity, "用户主动取消");
                break;
            case TAKEN:
                // 商家已接单，需要商家确认后，才能退款
                circulateToBusProcessCancel(orderEntity);
                break;
            default:
                throw new RRException(String.format("当前订单[%s],状态为[%s],无法取消", orderSn, orderStatusEnum.getDescription()));
        }

        sysNotificationService.onOrderCancel(orderEntity);
    }


    /**
     * 交由商家处理，
     * 并发送消息
     *
     * @param orderEntity
     */
    private void circulateToBusProcessCancel(OrderEntity orderEntity) {
        doUpdateOrderStatus(orderEntity.getId(), OrderStatusEnum.BUS_DEALING);
    }

    private void doCancel(Long orderId, String reason) {
        doUpdateOrderStatus(orderId, OrderStatusEnum.CANCEL);
    }

    @Transactional(rollbackFor = Exception.class)
    public void doCancelAndRefund(OrderEntity orderEntity, String reason) {
        doCancel(orderEntity.getId(), reason);
        doRefundAndMark(orderEntity, reason);
    }

    /**
     * 退款，并标记为已退款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doRefundAndMark(OrderEntity orderEntity, String reason) {
        boolean refundSuccess = true;
        // 支付宝退款
        String orderSn = orderEntity.getOrderSn();
        try {
            alipayService.refund(orderSn, reason);
        } catch (Exception e) {
            refundSuccess = false;

            log.warn("支付宝退款出错:[{}]", e.getMessage());
            e.printStackTrace();

        }

        orderEntity.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
        // 退款 状态
        if (refundSuccess) {
            orderEntity.setRefundStatus(OrderRefundEnum.YES.getCode());
        } else {
            orderEntity.setRefundStatus(OrderRefundEnum.NO.getCode());
        }

        updateById(orderEntity);


    }

    /**
     * 退款，并标记为已退款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doRefundAndMark(String orderSn, String reason) {
        OrderEntity orderEntity = getByOrderSn(orderSn);
        doRefundAndMark(orderEntity, reason);
    }

    @Override
    public void doUpdateOrderStatus(String orderSn, Integer code) {
        OrderStatusEnum statusEnum = OrderStatusEnum.getTypeByValue(code);
        OrderEntity orderEntity = getByOrderSn(orderSn);

        doUpdateOrderStatus(orderEntity.getId(), statusEnum);
    }

    @Override
    public OrderEntity getByOrderSn(String orderSn) {
        OrderEntity orderEntity = this.baseMapper.selectOne(
                new QueryWrapper<OrderEntity>()
                        .eq("order_sn", orderSn)
        );

        fixOrderStatusChinese(orderEntity);

        return orderEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrderEntity createOrderByDto(UserEntity userEntity, OrderDto orderDto) {
        Date now = new Date();
        // 订单号生成
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        long id = snowflake.nextId();

        OrderEntity orderEntity = new OrderEntity();

        orderEntity.setUserId(userEntity.getUserId());
        // orderEntity.setUserId(1L);
        orderEntity.setOrderSn(id + "");
        orderEntity.setReceiverName(orderDto.getReceiverName());
        orderEntity.setReceiverDetailAddress(orderDto.getReceiverDetailAddress());
        orderEntity.setReceiverPhone(orderDto.getReceiverPhone());
        orderEntity.setRemark(orderDto.getRemark());

        // 初始状态
        orderEntity.setOrderStatus(0);
        orderEntity.setConfirmStatus(0);
        orderEntity.setRefundStatus(0);
        orderEntity.setCreateTime(now);
        orderEntity.setUpdateTime(now);


        final BigDecimal[] orderTotalRealAmount = {new BigDecimal(0)};
        // 订单项处理
        List<OrderItemDto> orderItemDtoList = orderDto.getOrderItemEntities();
        List<OrderItemEntity> orderItemList = orderItemDtoList.stream().map(item -> {
            OrderItemEntity orderItemEntity = new OrderItemEntity();

            Long productId = item.getProductId();
            // 查询商品信息、分类
            ProductEntity productEntity = productService.getById(productId);
            if (productEntity == null) {
                throw new RRException("商品不存在，id：" + productId);
            }

            BigDecimal basePrice = productEntity.getBasePrice();
            orderItemEntity.setBasePrice(basePrice);


            StringBuilder saleAttrValueArray = new StringBuilder();
            BigDecimal extraPriceTotal = new BigDecimal(0);
            // 查询商品 销售属性
            List<Long> saleAttrIdList = item.getSaleAttrIdList();
            if (saleAttrIdList != null && saleAttrIdList.size() > 0) {
                for (Long saleAttrId : saleAttrIdList) {
                    // 通过商品id、销售属性id，查询关系
                    ProductSaleAttrRelationEntity saleAttrRelation = productSaleAttrRelationService.getByProductIdAndSaleAttrId(productId, saleAttrId);
                    if (saleAttrRelation != null) {
                        // 价格叠加
                        extraPriceTotal = extraPriceTotal.add(saleAttrRelation.getExtraPrice());

                        if (StringUtils.isNotBlank(saleAttrRelation.getSaleAttrFullName())) {
                            // 拼接saleAttrValueArray
                            saleAttrValueArray.append(saleAttrRelation.getSaleAttrFullName());
                            saleAttrValueArray.append(";");
                        }
                    }
                }

            }

            orderItemEntity.setOrderSn(id + "");
            orderItemEntity.setProductId(productId);
            orderItemEntity.setCategoryId(productEntity.getCategoryId());
            orderItemEntity.setNum(item.getNum());
            orderItemEntity.setSaleAttrValueArray(saleAttrValueArray.toString());
            orderItemEntity.setSaleAttrExtraPrice(extraPriceTotal);
            // 优惠
            orderItemEntity.setCouponAmount(new BigDecimal(0));

            // 总价= (基础价格+销售属性价格总和)* 数量 - 优惠
            BigDecimal realAmount = basePrice.add(extraPriceTotal).multiply(new BigDecimal(orderItemEntity.getNum()));
            orderItemEntity.setRealAmount(realAmount);
            orderItemEntity.setCreateTime(now);
            orderItemEntity.setUpdateTime(now);
            orderItemEntity.setProductName(productEntity.getName());

            //
            orderTotalRealAmount[0] = orderTotalRealAmount[0].add(realAmount);
            orderItemService.save(orderItemEntity);
            return orderItemEntity;
        }).collect(Collectors.toList());

        // 订单总价
        orderEntity.setOrderItemList(orderItemList);
        orderEntity.setRealAmount(orderTotalRealAmount[0]);


        // todo 前后端 价格验算
        // checkPrice(orderDto.getRealAmount(),orderTotalRealAmount[0]);

        // 保存订单
        save(orderEntity);
        return orderEntity;
    }

    @Override
    public OrderEntity getByOrderSnWithOrderItems(String orderSn) {
        OrderEntity orderEntity = getByOrderSn(orderSn);
        if (orderEntity == null) {
            return null;
        }
        // 查询订单项
        List<OrderItemEntity> list = orderItemService.queryByOrderSn(orderSn);
        orderEntity.setOrderItemList(list);

        return orderEntity;
    }


    @Override
    public List<OrderEntity> getByUserId(Long userId) {
        // 查询订单项
        List<OrderEntity> orderEntityList = this.baseMapper.selectList(
                new QueryWrapper<OrderEntity>()
                .eq("user_id", userId)
                        .orderByDesc("create_time")

        );
        orderEntityList.forEach(item -> {
            List<OrderItemEntity> itemEntityList = orderItemService.queryByOrderSn(item.getOrderSn());
            item.setOrderItemList(itemEntityList);

            fixOrderStatusChinese(item);
        });

        return orderEntityList;
    }

    @Override
    public PayVo getOrderPayVo(String orderSn) {
        OrderEntity orderEntity = getByOrderSnWithOrderItems(orderSn);
        if (orderEntity == null) {
            throw new RRException("订单不存在");
        }

        // 非新创建的订单
        if (!orderEntity.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
            throw new RRException("订单状态不正确");
        }
        PayVo payVo = new PayVo();
        payVo.setOut_trade_no(orderSn);
        // 订单主题
        String subject = buildOrderSubject(orderEntity);
        payVo.setSubject(subject);

        BigDecimal total = orderEntity.getRealAmount();
        payVo.setTotal_amount(total.toString());
        payVo.setBody("备注" + orderSn);

        return payVo;
    }

    @Override
    public String buildOrderSubject(OrderEntity orderEntityWithItems) {
        String subject = "";
        String productName = "";
        if (orderEntityWithItems.getOrderItemList() != null && orderEntityWithItems.getOrderItemList().size() > 0) {
            productName = orderEntityWithItems.getOrderItemList().get(0).getProductName();
        }

        if (StringUtils.isNotBlank(productName)) {
            subject = productName;
        }
        int orderItemSize = orderEntityWithItems.getOrderItemList().size();
        if (orderItemSize > 1) {
            subject += String.format("等%d件商品", orderItemSize);
        }
        return subject;
    }

    @Override
    public String buildOrderSubject(String orderSn) {
        OrderEntity withOrderItems = getByOrderSnWithOrderItems(orderSn);
        return buildOrderSubject(withOrderItems);
    }

    @Override
    public String handlePayResult(PayAsyncDto payAsyncDto) {
        String orderSn = payAsyncDto.getOut_trade_no();
        // 1. 保存交易流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setSubject(payAsyncDto.getSubject());
        paymentInfoEntity.setCallbackContent(JSONUtil.toJsonStr(payAsyncDto));

        paymentInfoEntity.setOrderSn(orderSn);
        paymentInfoEntity.setAlipayTradeNo(payAsyncDto.getTrade_no());
        paymentInfoEntity.setTotalAmount(new BigDecimal(payAsyncDto.getTotal_amount()));
        paymentInfoEntity.setPaymentStatus(payAsyncDto.getTrade_status());
        paymentInfoEntity.setCallbackTime(payAsyncDto.getNotify_time());
        paymentInfoEntity.setCreateTime(new Date());

        paymentInfoService.save(paymentInfoEntity);

        // 2.修改订单状态信息
        if (payAsyncDto.getTrade_status().equals("TRADE_SUCCESS") || payAsyncDto.getTrade_status().equals("TRADE_FINISHED")) {
            handlePaySuccess(orderSn);
        }
        return "success";
    }

    @Transactional(rollbackFor = Exception.class)
    public void handlePaySuccess(String orderSn) {
        // 支付成功
        this.doUpdateOrderStatus(orderSn, OrderStatusEnum.PAYED.getCode());

        // 自动接单
        ifAutoAcceptPayedOrder(orderSn);

        // 更新商品销量
        orderItemService.cumulativeSaleCountByOrderSn(orderSn);
    }


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        String key = (String) params.get("key");
        String orderStatus = (String) params.get("orderStatus");
        String orderConfirmStatus = (String) params.get("orderConfirmStatus");
        String startTime = (String) params.get("startTime");
        String endTime = (String) params.get("endTime");

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
                        .orderByDesc("create_time")
                        // 订单状态
                        .eq(StringUtils.isNotEmpty(orderStatus), "order_status", orderStatus)
                        .eq(StringUtils.isNotEmpty(orderConfirmStatus), "confirm_status", orderConfirmStatus)

                        // 时间范围
                        .ge(StringUtils.isNotEmpty(startTime), "create_time", startTime)
                        .le(StringUtils.isNotEmpty(endTime), "create_time", endTime)

                        .eq(StringUtils.isNotEmpty(key), "order_sn", key)
                        .or()
                        .like(StringUtils.isNotEmpty(key), "receiver_name", key)
                        .or()
                        .like(StringUtils.isNotEmpty(key), "receiver_detail_address", key)
                        .or()
                        .like(StringUtils.isNotEmpty(key), "receiver_phone", key)

        );

        for (OrderEntity record : page.getRecords()) {
            fixOrderStatusChinese(record);
        }
        return new PageUtils(page);
    }

    @Override
    public void userConfirmReceiving(Long userId, String orderSn) {
        OrderEntity orderEntity = getByOrderSn(orderSn);
        checkIllegal(orderEntity, userId);

        orderEntity.setConfirmStatus(OrderConfirmReceivingEnum.YES.getCode());

        updateById(orderEntity);
        // 用户收货，向商家发送通知
        sysNotificationService.onOrderConfirm(orderSn);
    }


    /**
     * 前后端 价格验算
     *
     * @param front
     * @param backend
     */
    private void checkPrice(BigDecimal front, BigDecimal backend) {
        if (!front.equals(backend)) {
            log.warn("订单金额发生变化");
            throw new RRException("订单金额发生变化");
        }
    }

    /**
     * 修改订单状态，必需要调用的方法，所以将通知逻辑放在这里
     *
     * @param orderId
     * @param statusEnum
     */
    private void doUpdateOrderStatus(Long orderId, OrderStatusEnum statusEnum) {
        OrderEntity orderEntity = getById(orderId);
        orderEntity.setOrderStatus(statusEnum.getCode());

        this.baseMapper.updateById(orderEntity);


        // 用户关注的内容：（商家操作的）
        // 商家关注的内容：用户操作的/其他管理员操作

        OrderStatusEnum orderStatusEnum = OrderStatusEnum.getTypeByValue(orderEntity.getOrderStatus());
        String content = "";
        String sysNotification = "";
        switch (orderStatusEnum) {
            case CANCEL:
                // 取消比较特殊，放在各自取消方法中
                break;
            case NEW:
                break;
            case PAYED:
                // 已付款，通知商家
                sysNotificationService.onOrderPayed(orderEntity);
                break;
            case TAKEN:
                // 商家已接单，通知用户
                userReminderService.onOrderTaken(orderEntity);
                break;
            case SEND_OUT:
                // 商家已送出，通知用户
                userReminderService.onOrderSendOut(orderEntity);
                break;
            case REACH:
                // 订单送达，通知用户
                userReminderService.onOrderSended(orderEntity);
                break;

            case BUS_DEALING:
                // 向商家发送 有新退款申请 消息
                sysNotificationService.onOrderUserCancelAndRefund(orderEntity);
                break;

            case REFUNDED:
                // 退款处理完成，通知用户
                userReminderService.onOrderRefunded(orderEntity);
                break;
            default:
                content = "您的订单状态为[未知]，请咨询商家";
                break;
        }
    }

    /**
     * 填充 订单状态的中文
     *
     * @param orderEntity
     */
    private void fixOrderStatusChinese(@Nullable OrderEntity orderEntity) {
        if (orderEntity == null) {
            return;
        }
        orderEntity.setOrderStatusChinese(OrderStatusEnum.getDescByCode(orderEntity.getOrderStatus()));
        orderEntity.setOrderConfirmStatusChinese(OrderConfirmReceivingEnum.getDescByCode(orderEntity.getConfirmStatus()));
        orderEntity.setOrderRefundStatusChinese(OrderRefundEnum.getDescByCode(orderEntity.getRefundStatus()));
    }

    @Override
    public void checkIllegal(OrderEntity orderEntity, Long userId) {
        if (orderEntity == null) {
            throw new RRException("订单不存在");
        }

        // 判断订单是否自己的
        if (!orderEntity.getUserId().equals(userId)) {
            throw new RRException("非法操作");
        }
    }

    @Override
    public void checkIllegal(String orderSn, Long userId) {
        OrderEntity orderEntity = getByOrderSn(orderSn);
        checkIllegal(orderEntity, userId);
    }


    @Override
    public void checkSatisfyAfterSale(String orderSn, Long userId) {
        OrderEntity orderEntity = getByOrderSn(orderSn);
        checkIllegal(orderEntity, userId);
        if (!OrderStatusEnum.REACH.getCode().equals(orderEntity.getOrderStatus())) {
            throw new RRException("该订单状态不正确，无法申请售后");
        }
    }

    @Override
    public void ifAutoAcceptPayedOrder(String orderSn) {
        log.info("自动接单：orderSn:[{}]", orderSn);
        String sysAutoAcceptOrder = sysConfigService.getValue(SYS_AUTO_ACCEPT_ORDER);
        // 自动接单
        if (StringUtils.isNotBlank(sysAutoAcceptOrder) && "1".equals(sysAutoAcceptOrder)) {
            doUpdateOrderStatus(orderSn, OrderStatusEnum.TAKEN.getCode());
        }

    }

    @Override
    public List<OrderCountVo> queryOrderCountByTimeUnit(ChartTimeUnitEnum chartTimeUnitEnum, String startTime, String endTime) {
        switch (chartTimeUnitEnum) {
            case MONTH:
                return this.baseMapper.queryOrderCountByMonth(startTime, endTime);
            case DAY:
                return this.baseMapper.queryOrderCountByDay(startTime, endTime);
            default:
                throw new RRException("暂时只支持day、month的时间模式");
        }

    }
}